package top.yangguangmc.smart_scheduling;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import top.yangguangmc.smart_scheduling.checks.ChecksManager;
import top.yangguangmc.smart_scheduling.pojo.Employee;
import top.yangguangmc.smart_scheduling.pojo.Shift;
import top.yangguangmc.smart_scheduling.utils.Utils;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.List;
import java.util.*;

public class SmartScheduling extends JFrame {
    public static final Logger logger = LogManager.getLogger();
    private static SmartScheduling instance;

    public static final String TITLE = "SmartScheduling";
    public static final int WIDTH = 1200;
    public static final int HEIGHT = 700;
    private JPanel rootPanel;
    private final ScheduleTable table;
    private final JPanel rightPanel;
    private final JLabel pathLabel;
    private EmployeeDialog employeeDialog;
    private ShiftDialog shiftDialog;
    private JTextArea checksOutputArea;

    private ChecksManager checksManager;
    private ConfigManager configManager;
    private IOManager ioManager;
    private SessionManager sessionManager;

    private boolean autoSave = true;

    public SmartScheduling() {
        // Initialize
        super();
        initBasic();
        initDataSession();

        // Menu
        JMenuBar menuBar = new JMenuBar();
        menuBar.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 14));
        rootPanel.add(menuBar, BorderLayout.NORTH);
        JMenu menu_File = new JMenu("文件 (F)");
        menu_File.setMnemonic(KeyEvent.VK_F);
        menuBar.add(menu_File);
        JMenuItem menuItem_Save = new JMenuItem("保存 (S)", new ImageIcon(ioManager.imageIconSave));
        menuItem_Save.setMnemonic(KeyEvent.VK_S);
        menuItem_Save.addActionListener(e -> saveAll());
        menu_File.add(menuItem_Save);
        JMenu menu_Sub_AutoSave = new JMenu("自动保存选项 (A)");
        menu_Sub_AutoSave.setMnemonic(KeyEvent.VK_A);
        JCheckBoxMenuItem menuItem_Checkbox_WhenLostFocus = new JCheckBoxMenuItem("当失去焦点时 (1)", true);
        menuItem_Checkbox_WhenLostFocus.setMnemonic(KeyEvent.VK_1);
        menuItem_Checkbox_WhenLostFocus.addChangeListener(e -> autoSave = menuItem_Checkbox_WhenLostFocus.getState());
        menu_Sub_AutoSave.add(menuItem_Checkbox_WhenLostFocus);
        menu_File.add(menu_Sub_AutoSave);
        JMenuItem menuItem_Exit = new JMenuItem("退出 (X)", new ImageIcon(ioManager.imageIconExit));
        menuItem_Exit.setMnemonic(KeyEvent.VK_X);
        menuItem_Exit.addActionListener(e -> {
            int answer = JOptionPane.showConfirmDialog(rootPanel,
                    "真的要退出吗？\n你的更改将被自动保存。",
                    "退出",
                    JOptionPane.YES_NO_OPTION);
            if (answer == JOptionPane.YES_OPTION) {
                stop();
            }
        });
        menu_File.add(menuItem_Exit);

        // Table
        table = new ScheduleTable();
        JScrollPane tableScrollPane = new JScrollPane(table);
        rootPanel.add(tableScrollPane, BorderLayout.CENTER);

        // Right panel
        rightPanel = new JPanel(new BorderLayout());
        rightPanel.setSize(150, HEIGHT);    //这句话似乎没有作用
        rightPanel.setBorder(BorderFactory.createLineBorder(new Color(30, 31, 34)));
        rootPanel.add(rightPanel, BorderLayout.EAST);
        JLabel title = new JLabel("                  工具栏                  ");
        title.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 18));
        title.setBackground(new Color(255, 255, 255));
        rightPanel.add(title, BorderLayout.NORTH);
        initToolPanel();

        //Down panel
        JPanel downPanel = new JPanel();
        downPanel.setLayout(new BorderLayout(3, 0));
        downPanel.setBorder(BorderFactory.createLineBorder(new Color(30, 31, 34)));
        rootPanel.add(downPanel, BorderLayout.SOUTH);
        JPanel downLeftPanel = new JPanel(new FlowLayout());
        downPanel.add(downLeftPanel, BorderLayout.WEST);
        JButton button_addLine = new JButton(new ImageIcon(ioManager.imageIconAddNewLine));
        button_addLine.setToolTipText("在底部增加新行");
        button_addLine.createToolTip();
        button_addLine.addActionListener(e -> table.addRow());
        downLeftPanel.add(button_addLine);
        JButton button_removeLine = new JButton(new ImageIcon(ioManager.imageIconRemoveLine));
        button_removeLine.setToolTipText("从底部移除一行");
        button_removeLine.createToolTip();
        button_removeLine.addActionListener(e -> table.removeRow());
        downLeftPanel.add(button_removeLine);
        pathLabel = new JLabel("");
        pathLabel.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
        pathLabel.setToolTipText("当前选中单元格的信息");
        pathLabel.createToolTip();
        downLeftPanel.add(pathLabel);
        JButton button_refresh = new JButton(new ImageIcon(ioManager.imageIconRefresh));
        button_refresh.setSize(40, 30);
        button_refresh.setToolTipText("刷新表格问题统计");
        button_refresh.createToolTip();
        button_refresh.addActionListener(e -> checksManager.scanProblems());
        downPanel.add(button_refresh, BorderLayout.EAST);

        // Other
        checksManager = new ChecksManager(table, sessionManager, checksOutputArea);

        // Finally
        validate();
        setVisible(true);
    }

    public static SmartScheduling getInstance() {
        if (instance == null) instance = new SmartScheduling();
        return instance;
    }

    private void initBasic() {
        logger.info("Initializing frame...");

        setTitle(TITLE);
        setAlwaysOnTop(true);
        setResizable(true);
        setSize(WIDTH, HEIGHT);
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        setLocation(Utils.centerXY(WIDTH, HEIGHT));
        CardLayout layout = new CardLayout();
        setLayout(layout);
        rootPanel = new JPanel(new BorderLayout());
        add(rootPanel);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                int answer = JOptionPane.showConfirmDialog(rootPanel,
                        "真的要退出吗？\n你的更改将被自动保存。",
                        "退出",
                        JOptionPane.YES_NO_OPTION);
                if (answer == JOptionPane.YES_OPTION) {
                    stop();
                }
            }

            @Override
            public void windowClosed(WindowEvent e) {
                saveAll();
            }
        });
        addWindowFocusListener(new WindowAdapter() {
            @Override
            public void windowLostFocus(WindowEvent e) {
                if (autoSave) {
                    saveAll();
                }
            }
        });

        Runtime.getRuntime().addShutdownHook(new Thread(this::saveAll, "SavingThread"));

    }

    private void initToolPanel() {
        logger.info("Initializing tool panel...");

        JPanel toolPanel = new JPanel(null);
        rightPanel.add(toolPanel, BorderLayout.CENTER);
        employeeDialog = new EmployeeDialog();
        employeeDialog.refreshEmployeeTable();
        shiftDialog = new ShiftDialog();
        shiftDialog.refreshShiftTable();

        JPanel manageElementPanel = new JPanel(null);
        manageElementPanel.setBounds(5, 5, 225, 100);
        manageElementPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(
                new Color(67, 69, 74)), "管理配置"));
        toolPanel.add(manageElementPanel);

        JButton button_manageEmployee = new JButton("管理员工");
        button_manageEmployee.setBounds(50, 18, 130, 30);
        button_manageEmployee.addActionListener(e -> {
            employeeDialog.updateXY();
            employeeDialog.setVisible(true);
        });
        manageElementPanel.add(button_manageEmployee);

        JButton button_manageShift = new JButton("管理班次");
        button_manageShift.setBounds(50, 55, 130, 30);
        button_manageShift.addActionListener(e -> {
            shiftDialog.updateXY();
            shiftDialog.setVisible(true);
        });
        manageElementPanel.add(button_manageShift);

        checksOutputArea = new JTextArea();
        checksOutputArea.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
        checksOutputArea.setEditable(false);
        checksOutputArea.setLineWrap(true);
        checksOutputArea.setWrapStyleWord(true);
        JScrollPane scrollPane_outputArea = new JScrollPane(checksOutputArea);
        scrollPane_outputArea.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(new Color(30, 31, 34)), "错误与警告"));
        rightPanel.add(scrollPane_outputArea, BorderLayout.SOUTH);
        scrollPane_outputArea.setPreferredSize(new Dimension(150, 300));

    }

    public void stop() {
        logger.info("Stopping!");
        dispose();
        System.exit(0);
    }

    public void saveAll() {
        logger.info("Saving all data...");

    }

    public static void main(String[] args) {
        logger.info("Program started at " + new Date() + ".");
        getInstance().setAlwaysOnTop(false);
    }

    private void initDataSession() {
        logger.info("Initializing session...");
        sessionManager = new SessionManager();
        try {
            ioManager = new IOManager();
        } catch (IOException e) {
            logger.error("Failed to load IOManager!", e);
            showErrDialog(this, "加载IOManager失败！ 请尝试重启软件！");
            System.exit(1);
        }
        configManager = new ConfigManager();
    }

    public ChecksManager getChecksManager() {
        return checksManager;
    }

    public IOManager getIOManager() {
        return ioManager;
    }

    public SessionManager getSessionManager() {
        return sessionManager;
    }

    private SmartScheduling getReference() {
        return this;
    }

    public ConfigManager getConfigManager() {
        return configManager;
    }

    public static class ScheduleTableModel extends DefaultTableModel {
        public ScheduleTableModel() {
            super(25, 10);
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            //第1行除了第4格、第2行、第1列不可编辑
            return (row != 0 || column == 3) && row != 1 && column != 0;
        }

    }

    public static class SimpleUneditableTableModel extends DefaultTableModel {

        public SimpleUneditableTableModel(int rowCount, int columnCount) {
            super(rowCount, columnCount);
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }

    }

    public class EmployeeDialog extends JDialog {

        private final Dimension dialogDim;
        private final JTable employeeTable;
        public JButton button_addEmployee;
        public JButton button_editEmployee;
        public JButton button_delEmployee;
        private final AddEmployeeDialog dialog_addEmployee;
        private final EditEmployeeDialog dialog_editEmployee;
        public EmployeeDialog() {
            super(getReference(), "管理员工", true);
            logger.info("Initializing employee dialog...");
            setLayout(null);
            setResizable(false);
            dialogDim = new Dimension(340, 560);
            setSize(dialogDim);
            updateXY();
            setDefaultCloseOperation(HIDE_ON_CLOSE);

            List<Employee> employeeList = new ArrayList<>(sessionManager.getEmployees());
            employeeTable = new JTable(new SimpleUneditableTableModel(employeeList.size(), 2));
            JScrollPane employeeTableScrollPane = new JScrollPane(employeeTable);
            employeeTableScrollPane.setBounds(10, 10, 300, 400);
            add(employeeTableScrollPane);
            employeeTable.setFont(new Font(Font.DIALOG, Font.PLAIN, 18));
            employeeTable.setRowHeight(24);
            employeeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

            dialog_addEmployee = new AddEmployeeDialog();
            dialog_editEmployee = new EditEmployeeDialog();

            button_editEmployee = new JButton("修改此员工");
            button_editEmployee.setBounds(10, 412, 140, 40);
            button_editEmployee.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_editEmployee.addActionListener(e -> {
                int selectedRow = employeeTable.getSelectedRow();
                if (selectedRow <= 0) return;
                Object value = employeeTable.getValueAt(selectedRow, 0);
                if (value instanceof Integer) {
                    int id = (Integer) value;
                    dialog_editEmployee.display(sessionManager.getEmployee(id));
                }
            });
            add(button_editEmployee);

            button_delEmployee = new JButton("删除此员工");
            button_delEmployee.setBounds(170, 412, 140, 40);
            button_delEmployee.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_delEmployee.addActionListener(e -> {
                int selectedRow = employeeTable.getSelectedRow();
                if (selectedRow <= 0) return;
                Object value = employeeTable.getValueAt(selectedRow, 0);
                if (value instanceof Integer) {
                    int id = (Integer) value;
                    String name = sessionManager.getEmployee(id).getName();
                    boolean success = sessionManager.deleteEmployee(id);
                    if (success)
                        logger.info("Successfully deleted employee (id=" + id + ", name=\"" + name + "\").");
                    else
                        logger.error("Failed to delete employee (id=" + id + ", name=\"" + name + "\"). Collection contains: " + sessionManager.getEmployeesAsString());
                    refreshEmployeeTable();
                }
            });

            add(button_delEmployee);
            button_addEmployee = new JButton("添加新员工");
            button_addEmployee.setBounds(90, 460, 140, 40);
            button_addEmployee.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_addEmployee.addActionListener(e -> dialog_addEmployee.display());
            add(button_addEmployee);

            JButton button_refresh = new JButton(new ImageIcon(ioManager.imageIconRefresh));
            button_refresh.setBounds(275, 475, 40, 40);
            button_refresh.setToolTipText("刷新此表格");
            button_refresh.createToolTip();
            button_refresh.addActionListener(e -> refreshEmployeeTable());
            add(button_refresh);

        }

        protected JDialog getEmployeeDialogInstance() {
            return this;
        }

        private void refreshEmployeeTable() {
            logger.info("Refreshing employee table in employee dialog...");

            List<Employee> employeeList = new ArrayList<>(sessionManager.getEmployees());
            DefaultTableModel tableModel = (DefaultTableModel) employeeTable.getModel();
            tableModel.setRowCount(employeeList.size() + 1);
            employeeTable.setValueAt("工号", 0, 0);
            employeeTable.setValueAt("姓名", 0, 1);
            employeeList.sort(Comparator.comparingInt(Employee::getId));
            for (int i = 0; i < employeeList.size(); i++) {
                Employee emp = employeeList.get(i);
                employeeTable.setValueAt(emp.getId(), i + 1, 0);
                employeeTable.setValueAt(emp.getName(), i + 1, 1);
            }
            employeeTable.validate();
        }

        public void updateXY() {
            setLocation(Utils.centerXY(dialogDim.width, dialogDim.height, getReference()));
            validate();
        }

        public class AddEmployeeDialog extends JDialog {

            private final Dimension subDialogDim;
            public JTextField textField_ID;
            public JTextField textField_Name;
            public JLabel label_Notice;
            public AddEmployeeDialog() {
                super(getEmployeeDialogInstance(), "添加新员工", true);
                logger.info("Initializing addEmployee sub-dialog...");
                setLayout(null);
                setResizable(false);
                subDialogDim = new Dimension(285, 200);
                setSize(subDialogDim);
                updateXY();
                setDefaultCloseOperation(HIDE_ON_CLOSE);

                JLabel label_ID = new JLabel("员工工号：");
                label_ID.setBounds(5, 30, 100, 30);
                label_ID.setFont(new Font(Font.DIALOG, Font.PLAIN, 16));
                add(label_ID);
                JLabel label_Name = new JLabel("员工名：");
                label_Name.setBounds(5, 65, 100, 30);
                label_Name.setFont(new Font(Font.DIALOG, Font.PLAIN, 16));
                add(label_Name);
                label_Notice = new JLabel("");
                label_Notice.setBounds(5, 3, 250, 30);
                label_Notice.setForeground(new Color(255, 0, 0));
                label_Notice.setFont(new Font(Font.DIALOG, Font.BOLD, 15));
                add(label_Notice);

                textField_ID = new JTextField();
                textField_ID.setBounds(100, 30, 150, 30);
                textField_ID.setFont(new Font(Font.DIALOG_INPUT, Font.PLAIN, 15));
                add(textField_ID);
                textField_Name = new JTextField();
                textField_Name.setBounds(100, 65, 150, 30);
                textField_Name.setFont(new Font(Font.DIALOG_INPUT, Font.PLAIN, 15));
                add(textField_Name);

                JButton button_OK = getButtonOk();
                add(button_OK);

                addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        resetInput();
                    }
                });

            }

            @NotNull
            private JButton getButtonOk() {
                JButton button_OK = new JButton("确定");
                button_OK.setBounds(80, 100, 100, 40);
                button_OK.setFont(new Font(Font.DIALOG, Font.PLAIN, 15));
                button_OK.addActionListener(e -> {
                    String idString = textField_ID.getText();
                    String name = textField_Name.getText();
                    if (Utils.isNullOrEmpty(idString)) {
                        showErrDialog(this, "“工号”不能为空！");
                        label_Notice.setText("“工号”不能为空！");
                        textField_ID.setText("");
                        return;
                    }
                    if (Utils.isNullOrEmpty(name)) {
                        showErrDialog(this, "“姓名”不能为空！");
                        label_Notice.setText("“姓名”不能为空！");
                        textField_Name.setText("");
                        return;
                    }
                    int id;
                    try {
                        id = Integer.parseInt(idString);
                    } catch (NumberFormatException ex) {
                        showErrDialog(this, "“工号”不是有效的整数！");
                        label_Notice.setText("“工号”不是有效的整数！");
                        textField_ID.setText("");
                        return;
                    }
                    Employee emp = sessionManager.getEmployee(id);
                    if (emp != null) {
                        showErrDialog(this, "已经存在工号为" + id + "的员工！");
                        return;
                    }
                    sessionManager.addEmployee(new Employee(id, name));
                    logger.info("Successfully added a new employee (id=" + id + ", name=\"" + name + "\").");
                    setVisible(false);
                    resetInput();
                    refreshEmployeeTable();
                });
                return button_OK;
            }

            private void resetInput() {
//                logger.info("Input reset in dialog_addEmployee.");
                label_Notice.setText("");
                textField_ID.setText("");
                textField_Name.setText("");
                updateXY();
            }

            public void display() {
                setVisible(true);
            }

            public void updateXY() {
                setLocation(Utils.centerXY(subDialogDim.width, subDialogDim.height, getEmployeeDialogInstance()));
            }

        }
        public class EditEmployeeDialog extends JDialog {

            private final Dimension subDialogDim;
            public final JTextField textField_ID;
            public final JTextField textField_Name;
            public final JLabel label_Notice;
            private final JLabel label_OldData;
            private Employee isBeingEditedEmployee;

            public EditEmployeeDialog() {
                super(getEmployeeDialogInstance(), "修改员工", true);
                logger.info("Initializing editEmployee sub-dialog...");
                setLayout(null);
                setResizable(false);
                subDialogDim = new Dimension(285, 240);
                setSize(subDialogDim);
                updateXY();
                setDefaultCloseOperation(HIDE_ON_CLOSE);

                JLabel label_ID = new JLabel("员工工号：");
                label_ID.setBounds(5, 60, 100, 30);
                label_ID.setFont(new Font(Font.DIALOG, Font.PLAIN, 16));
                add(label_ID);
                JLabel label_Name = new JLabel("员工名：");
                label_Name.setBounds(5, 95, 100, 30);
                label_Name.setFont(new Font(Font.DIALOG, Font.PLAIN, 16));
                add(label_Name);
                label_Notice = new JLabel("");
                label_Notice.setBounds(5, 33, 250, 30);
                label_Notice.setForeground(new Color(255, 0, 0));
                label_Notice.setFont(new Font(Font.DIALOG, Font.BOLD, 15));
                add(label_Notice);
                label_OldData = new JLabel("");
                label_OldData.setBounds(5, 3, 250, 30);
                label_OldData.setFont(new Font(Font.DIALOG, Font.BOLD, 15));
                add(label_OldData);

                textField_ID = new JTextField();
                textField_ID.setBounds(100, 60, 150, 30);
                textField_ID.setFont(new Font(Font.DIALOG_INPUT, Font.PLAIN, 15));
                add(textField_ID);
                textField_Name = new JTextField();
                textField_Name.setBounds(100, 95, 150, 30);
                textField_Name.setFont(new Font(Font.DIALOG_INPUT, Font.PLAIN, 15));
                add(textField_Name);

                JButton button_OK = getButtonOk();
                add(button_OK);

                addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        resetInput();
                    }
                });

            }

            @NotNull
            private JButton getButtonOk() {
                JButton button_OK = new JButton("确定");
                button_OK.setBounds(80, 130, 100, 40);
                button_OK.setFont(new Font(Font.DIALOG, Font.PLAIN, 15));
                button_OK.addActionListener(e -> {
                    String idString = textField_ID.getText();
                    String name = textField_Name.getText();
                    if (Utils.isNullOrEmpty(idString)) {
                        showErrDialog(this, "“工号”不能为空！");
                        label_Notice.setText("“工号”不能为空！");
                        textField_ID.setText("");
                        return;
                    }
                    if (Utils.isNullOrEmpty(name)) {
                        showErrDialog(this, "“姓名”不能为空！");
                        label_Notice.setText("“姓名”不能为空！");
                        textField_Name.setText("");
                        return;
                    }
                    int id;     //这里的ID是新的（用户刚输入的）
                    try {
                        id = Integer.parseInt(idString);
                    } catch (NumberFormatException ex) {
                        showErrDialog(this, "“工号”不是有效的整数！");
                        label_Notice.setText("“工号”不是有效的整数！");
                        textField_ID.setText("");
                        return;
                    }

                    //暂时删除自己（凭借旧id），方便查询。对象的应用依然保存在 isBeingEditedEmployee。
                    sessionManager.deleteEmployee(isBeingEditedEmployee.getId());
                    if (sessionManager.getEmployee(id) != null) {   //如果新ID可以找到对应对象，说明新ID非法
                        showErrDialog(this, "工号" + id + "已经被另一个员工占用了！");
                        label_Notice.setText("工号" + id + "已经被员工" + sessionManager.getEmployee(id).getName() + "占用了！");
                        textField_ID.setText("");
                        sessionManager.addEmployee(isBeingEditedEmployee);
                        return;
                    }

                    //能运行到这里，说明测试通过，可以覆盖旧数据并重新添加回集合
                    isBeingEditedEmployee.setId(id);
                    isBeingEditedEmployee.setName(name);
                    sessionManager.addEmployee(isBeingEditedEmployee);

                    logger.info("Successfully edited employee (id=" + id + ", name=\"" + name + "\").");
                    setVisible(false);
                    resetInput();
                    refreshEmployeeTable();
                });
                return button_OK;
            }

            private void resetInput() {
//                logger.info("Input reset in dialog_editEmployee.");
                label_Notice.setText("");
                textField_ID.setText("");
                textField_Name.setText("");
                updateXY();
            }

            public void display(Employee beShowedEmployee) {
                isBeingEditedEmployee = beShowedEmployee;
                int id = beShowedEmployee.getId();
                String name = beShowedEmployee.getName();
                textField_ID.setText(Integer.valueOf(id).toString());
                textField_Name.setText(name);
                label_Notice.setText("请输入该员工的新信息");
                label_OldData.setText(" · 旧数据：[工号=" + id + ",  姓名=“" + name + "”]");
                setVisible(true);
            }

            public void updateXY() {
                setLocation(Utils.centerXY(subDialogDim.width, subDialogDim.height, getEmployeeDialogInstance()));
            }

        }

    }
    public static void showErrDialog(Component parent, Object message) {
        JOptionPane.showMessageDialog(parent, message, "错误", JOptionPane.ERROR_MESSAGE);
    }

    public class ShiftDialog extends JDialog {

        private final Dimension dialogDim;
        private final JTable shiftTable;
        public JButton button_addShift;
        public JButton button_editShift;
        public JButton button_delShift;
        public ShiftDialog() {
            super(getReference(), "管理员工", true);
            logger.info("Initializing shift dialog...");
            setLayout(null);
            setResizable(false);
            dialogDim = new Dimension(340, 560);
            setSize(dialogDim);
            updateXY();
            setDefaultCloseOperation(HIDE_ON_CLOSE);

            List<Shift> shiftList = new ArrayList<>(sessionManager.getShifts());
            shiftTable = new JTable(new SimpleUneditableTableModel(shiftList.size(), 1));
            JScrollPane shiftTableScrollPane = new JScrollPane(shiftTable);
            shiftTableScrollPane.setBounds(10, 10, 300, 400);
            add(shiftTableScrollPane);
            shiftTable.setFont(new Font(Font.DIALOG, Font.PLAIN, 18));
            shiftTable.setRowHeight(24);
            shiftTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

            button_editShift = new JButton("修改此班次");
            button_editShift.setBounds(10, 412, 140, 40);
            button_editShift.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_editShift.addActionListener(e -> {
                int selectedRow = shiftTable.getSelectedRow();
                if (selectedRow <= 0) return;
                Object value = shiftTable.getValueAt(selectedRow, 0);
                if (value instanceof String) {
                    String oldName = (String) value;
//                    dialog_editShift.display(beansManager.getShift(name));
                    String newName = JOptionPane.showInputDialog(getShiftDialogInstance(), "请输入新班次的名称： （旧名称：“" + oldName + "”）", "添加新班次", JOptionPane.PLAIN_MESSAGE);
                    if (Utils.isNullOrEmpty(newName)) {
                        showErrDialog(this, "修改失败！班次名称不能为空。");
                        return;
                    }
                    sessionManager.deleteShift(oldName);
                    Shift shift = sessionManager.getShift(newName);
                    if (shift != null) {
                        showErrDialog(this, "修改失败！已经存在名称为“" + newName + "”的班次。");
                        sessionManager.addShift(new Shift(oldName));
                        return;
                    }
                    sessionManager.addShift(new Shift(newName));
                    logger.info("Successfully edited a shift from \"" + oldName + "\" to \"" + newName + "\".");
                    refreshShiftTable();
                }
            });
            add(button_editShift);

            button_delShift = new JButton("删除此班次");
            button_delShift.setBounds(170, 412, 140, 40);
            button_delShift.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_delShift.addActionListener(e -> {
                int selectedRow = shiftTable.getSelectedRow();
                if (selectedRow <= 0) return;
                Object value = shiftTable.getValueAt(selectedRow, 0);
                if (value instanceof String) {
                    String name = (String) value;
                    boolean success = sessionManager.deleteShift(name);
                    if (success)
                        logger.info("Successfully deleted shift \"" + name + "\".");
                    else
                        logger.error("Failed to delete shift \"" + name + "\". Collection contains: " + sessionManager.getShiftsAsString());
                    refreshShiftTable();
                }
            });

            add(button_delShift);
            button_addShift = new JButton("添加新班次");
            button_addShift.setBounds(90, 460, 140, 40);
            button_addShift.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
            button_addShift.addActionListener(e -> {
//                dialog_addShift.display();
                String name = JOptionPane.showInputDialog(getShiftDialogInstance(), "请输入新班次的名称：", "添加新班次", JOptionPane.PLAIN_MESSAGE);
                if (Utils.isNullOrEmpty(name)) {
                    showErrDialog(this, "添加失败！班次名称不能为空。");
                    return;
                }
                Shift shift = sessionManager.getShift(name);
                if (shift != null) {
                    showErrDialog(this, "添加失败！已经存在名称为“" + name + "”的班次。");
                    return;
                }
                sessionManager.addShift(new Shift(name));
                logger.info("Successfully added a new shift \"" + name + "\".");
                refreshShiftTable();
            });
            add(button_addShift);

            JButton button_refresh = new JButton(new ImageIcon(ioManager.imageIconRefresh));
            button_refresh.setBounds(275, 475, 40, 40);
            button_refresh.setToolTipText("刷新此表格");
            button_refresh.createToolTip();
            button_refresh.addActionListener(e -> refreshShiftTable());
            add(button_refresh);
        }

        protected JDialog getShiftDialogInstance() {
            return this;
        }

        private void refreshShiftTable() {
            logger.info("Refreshing shift table in shift dialog...");

            List<Shift> list = new ArrayList<>(sessionManager.getShifts());
            list.sort(Comparator.comparing(Shift::getName));
            list.remove(Shift.EMPTY);   //排除默认项 “空”
            DefaultTableModel tableModel = (DefaultTableModel) shiftTable.getModel();
            tableModel.setRowCount(list.size() + 1);
            shiftTable.setValueAt("     【班次名】", 0, 0);
            for (int i = 0; i < list.size(); i++) {
                String name = list.get(i).getName();
                shiftTable.setValueAt(name, i + 1, 0);
            }
            shiftTable.validate();
        }

        public void updateXY() {
            setLocation(Utils.centerXY(dialogDim.width, dialogDim.height, getReference()));
            validate();
        }

    }

    public class ScheduleTable extends JTable {

        private boolean showRemoveLineDialog = true;
        private final JPopupMenu rightClickMenu_Employees;
        private final JPopupMenu rightClickMenu_Shifts;
        private final JPopupMenu rightClickMenu_Date;
        private final DateSelectorDialog dialog_DateSelector;
        private boolean isProgrammedUpdate = false;
        public ScheduleTable() {
            super(new ScheduleTableModel());

            logger.info("Initializing root table...");
            setRowHeight(32);
            setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18));
            setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            setCellSelectionEnabled(true);

            dialog_DateSelector = new DateSelectorDialog();

            setDefaultContents();
            TableCellRenderer cellRenderer = new SpecialCellRenderer();
            setDefaultRenderer(Object.class, cellRenderer);

            rightClickMenu_Employees = new JPopupMenu("选择员工...");
            rightClickMenu_Shifts = new JPopupMenu("选择班次...");
            rightClickMenu_Date = new JPopupMenu("选择日期...");
            updateRightClickMenu();
            JMenuItem menuItem_SelectDate = new JMenuItem("选择一个日期...", new ImageIcon(ioManager.imageIconCalendar));
            menuItem_SelectDate.addActionListener(e -> dialog_DateSelector.display());
            rightClickMenu_Date.add(menuItem_SelectDate);

            addMouseListener(new PopupMouseListener());

            getModel().addTableModelListener(e -> {
                if (e.getType() == TableModelEvent.UPDATE) {
                    if (!isProgrammedUpdate) {
                        checksManager.scanProblems();
                    }
                }
            });

            getSelectionModel().addListSelectionListener(e -> {
                isProgrammedUpdate = false;

                int row = getSelectedRow();
                if (row > 1) {
                    Object objEmp = getValueAt(row, 1);
                    String strEmp = "员工 ";
                    StringBuilder strShift = new StringBuilder("，班次：");
                    if (objEmp != null) {
                        Employee employee = sessionManager.getEmployee(objEmp.toString());
                        if (employee != null) {
                            strEmp += employee.toString();
                            for (int column = 3; column < getColumnCount(); column++) {
                                Object objShift = getValueAt(row, column);
                                if (objShift != null) {
                                    Shift shift = sessionManager.getShift(objShift.toString());
                                    if (shift != null) {
                                        strShift.append(shift);
                                        strShift.append("、");
                                    }
                                }
                            }
                            if (strShift.toString().endsWith("、")) strShift.deleteCharAt(strShift.length() - 1);
                        } else strEmp = "<未输入>";
                    } else strShift = new StringBuilder(" <未输入>");

                    pathLabel.setText(" [第" + (row - 1) + "行] " + strEmp + strShift);
                } else {
                    pathLabel.setText(" [第" + (row - 1) + "行] ");
                }
            });

        }

        public void setProgrammedUpdate(boolean programmedUpdate) {
            isProgrammedUpdate = programmedUpdate;
        }

        protected void setDefaultContents() {
            logger.info("Initializing root table default contents...");
            setValueAt("序号", 0, 0);
            setValueAt("姓名", 0, 1);
            setValueAt("工号", 0, 2);
            setValueAt("No.", 1, 0);
            setValueAt("Name", 1, 1);
            setValueAt("ID", 1, 2);

            setValueAt("<日期1>", 0, 3);
            setValueAt("<日期2>", 0, 4);
            setValueAt("<日期3>", 0, 5);
            setValueAt("<日期4>", 0, 6);
            setValueAt("<日期5>", 0, 7);
            setValueAt("<日期6>", 0, 8);
            setValueAt("<日期7>", 0, 9);

            setValueAt("<星期1>", 1, 3);
            setValueAt("<星期2>", 1, 4);
            setValueAt("<星期3>", 1, 5);
            setValueAt("<星期4>", 1, 6);
            setValueAt("<星期5>", 1, 7);
            setValueAt("<星期6>", 1, 8);
            setValueAt("<星期7>", 1, 9);

            for (int i = 2; i <= 24; i++) {
                setValueAt(i - 1, i, 0);
            }

        }

        public void showRightClickMenu(int row, int column, Component from) {
            Rectangle cellRect = getCellRect(row, column, true);
            updateRightClickMenu();
            //判断该显示哪一个菜单
            if ((row == 0 || row == 1) && column == 3) {
                rightClickMenu_Date.show(from, cellRect.x + cellRect.width, cellRect.y);
            } else if (row > 1 && column <= 2 && column != 0) {
                rightClickMenu_Employees.show(from, cellRect.x + cellRect.width, cellRect.y);
            } else if (row > 1 && column != 0) {
                rightClickMenu_Shifts.show(from, cellRect.x + cellRect.width, cellRect.y);
            }
        }

        public void updateRightClickMenu() {

            //  Employee
            //移除原有的组件
            for (Component comp : rightClickMenu_Employees.getComponents()) {
                if (comp instanceof JMenuItem) {
                    rightClickMenu_Employees.remove(comp);
                }
            }
            //添加新组件
            for (Employee employee : sessionManager.getEmployees()) {
                SpecialMenuItem item = new SpecialMenuItem(employee);
                item.addActionListener(e -> completeCell(item.getBindTo()));
                rightClickMenu_Employees.add(item);
            }

            //  Shift
            for (Component comp : rightClickMenu_Shifts.getComponents()) {
                if (comp instanceof JMenuItem) {
                    rightClickMenu_Shifts.remove(comp);
                }
            }
            for (Shift shift : sessionManager.getShifts()) {
                SpecialMenuItem item = new SpecialMenuItem(shift);
                item.addActionListener(e -> completeCell(item.getBindTo()));
                rightClickMenu_Shifts.add(item);
            }


        }


        public void addRow() {
            DefaultTableModel model = (DefaultTableModel) getModel();
            int rowCount = model.getRowCount();
            model.setRowCount(rowCount + 1);
            rowCount++;
            setValueAt(rowCount - 2, rowCount - 1, 0);
        }

        public void removeRow() {
            DefaultTableModel model = (DefaultTableModel) getModel();
            int rowCount = model.getRowCount();
            if (rowCount > 2) {
                if (showRemoveLineDialog) {
                    String s = (String) model.getValueAt(rowCount - 1, 1);
                    if (s != null && !s.isEmpty()) {
                        int result = JOptionPane.showConfirmDialog(SmartScheduling.getInstance(),
                                "移除最后一行将会移除其中的所有信息，确定要移除吗？\n此消息不会此这次回话中显示第二次。",
                                "确定移除最后一行？", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
                        if (result == JOptionPane.OK_OPTION) {
                            model.removeRow(rowCount - 1);
                        }
                        showRemoveLineDialog = false;
                    } else model.removeRow(rowCount - 1);
                } else model.removeRow(rowCount - 1);
            } else {
                JOptionPane.showMessageDialog(SmartScheduling.getInstance(),
                        "你不能删除表头！", "无法删除行", JOptionPane.ERROR_MESSAGE);
            }
        }

        public void completeCell(Object objectInMenuItem) {
            if (objectInMenuItem instanceof Employee) {
                Employee employee = (Employee) objectInMenuItem;
                int row = getSelectedRow();
                int column = getSelectedColumn();
                if (row > 1) {
                    if (column == 1) {
                        setValueAt(employee.getName(), row, 1);
                        setValueAt(employee.getId(), row, 2);
                    } else if (column == 2) {
                        setValueAt(employee.getName(), row, 1);
                        setValueAt(employee.getId(), row, 2);
                    }
                }
            } else if (objectInMenuItem instanceof Shift) {
                Shift shift = (Shift) objectInMenuItem;
                int row = getSelectedRow();
                int column = getSelectedColumn();
                if (row > 1 && column > 2) {
                    setValueAt(shift.getName(), row, column);
                }
            }
            checksManager.scanProblems();
        }

        public class SpecialMenuItem extends JMenuItem {

            private final Object bindTo;
            public SpecialMenuItem(Employee employee) {
                super(employee.toString());
                bindTo = employee;
            }

            public SpecialMenuItem(Shift shift) {
                super(shift.toString());
                bindTo = shift;
            }

            public Object getBindTo() {
                return bindTo;
            }

        }
        private class PopupMouseListener extends MouseAdapter {

            @Override
            public void mousePressed(@NotNull MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) { //左键双击
                    if (e.getClickCount() >= 2) {
                        maybeShowPopup(e);
                    }
                } else if (e.getButton() == MouseEvent.BUTTON3) {  //右键
                    try {
                        //模拟一次左键点击，来切换选中的单元格
                        Robot robot = new Robot();
                        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                        Thread.sleep(5);
                        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                        Thread.sleep(50);

                        maybeShowPopup(e);
                    } catch (AWTException ex) {
                        logger.error("Error when simulating mouse lift click!", ex);
                    } catch (InterruptedException ex) {
                        logger.error("Error when performing Thread.sleep() !", ex);
                    } finally {
                        try {
                            new Robot().mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                        } catch (AWTException ignored) {
                        }
                    }
                }
            }
            @Override
            public void mouseReleased(MouseEvent e) {
                maybeShowPopup(e);
            }

            private void maybeShowPopup(@NotNull MouseEvent e) {
                if (e.isPopupTrigger()) {
                    showRightClickMenu(getSelectedRow(), getSelectedColumn(), e.getComponent());
                }
            }


        }
        public class SpecialCellRenderer extends DefaultTableCellRenderer {

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                if (value != null) {
                    if ((row == 0 && column == 0) || (row == 0 && column == 1) || (row == 0 && column == 2)
                            || (row == 1 && column == 0) || (row == 1 && column == 1) || (row == 1 && column == 2)
                    ) {
                        JLabel label = new JLabel(String.valueOf(value));
                        Rectangle rect = table.getCellRect(row, column, true);
                        rect.setSize(rect.width, rect.height);
                        label.setBounds(rect);
                        label.setHorizontalAlignment(SwingConstants.CENTER);
                        label.setBackground(new Color(215, 215, 215));
                        Font font = table.getFont();
                        if (row == 0) {
                            label.setFont(new Font("黑体", Font.BOLD, font.getSize() + 1));
                        } else {
                            label.setFont(new Font("黑体", font.getStyle(), font.getSize() - 1));
                        }
                        return label;
                    } else if (value.toString().equals(Shift.EMPTY.toString())) {
                        String s = value.toString();
                        s = "        " + s;
                        return super.getTableCellRendererComponent(table, s, isSelected, hasFocus, row, column);
                    } else
                        return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                } else
                    return super.getTableCellRendererComponent(table, null, isSelected, hasFocus, row, column);
            }
        }
        public class DateSelectorDialog extends JDialog {

            private final Dimension dialogDim;
            private final JTextField textField_Year;
            private final JComboBox<String> comboBox_Month;
            private final JComboBox<Integer> comboBox_Day;
            public DateSelectorDialog() {
                super(getReference(), "选择一个日期", true);
                logger.info("Initializing date selector dialog...");
                setLayout(null);
                setResizable(false);
                dialogDim = new Dimension(360, 150);
                setSize(dialogDim);
                updateXY();
                setDefaultCloseOperation(HIDE_ON_CLOSE);

                textField_Year = new JTextField();
                textField_Year.setBounds(5, 25, 100, 30);
                add(textField_Year);

                comboBox_Month = getComboBox_month();
                comboBox_Month.setBounds(115, 25, 100, 30);
                add(comboBox_Month);

                comboBox_Day = new JComboBox<>();
                comboBox_Day.setBounds(225, 25, 100, 30);
                for (int i = 1; i <= 31; i++) {
                    comboBox_Day.addItem(i);
                }
                add(comboBox_Day);

                JButton button_OK = new JButton("确定");
                button_OK.setBounds(130, 60, 80, 40);
                button_OK.addActionListener(e -> {
                    try {
                        int year = Integer.parseInt(textField_Year.getText().trim());
                        int month = comboBox_Month.getSelectedIndex() + 1;
                        int day = comboBox_Day.getSelectedIndex() + 1;
                        if (month > 0 && day > 0) {
                            if (month == 4 || month == 6 || month == 9 || month == 11) {
                                if (day > 30) {
                                    showErrDialog(this, "小月天数不超过30天！");
                                    return;
                                }
                            } else if (month == 2) {
                                if ((year % 4 == 0) && (year % 400 != 0)) {    //是闰年
                                    if (day > 29) {
                                        showErrDialog(this, year + "年是闰年，二月天数不超过29天！");
                                        return;
                                    }
                                } else {
                                    if (day > 28) {
                                        showErrDialog(this, year + "年不是闰年，二月天数不超过28天！");
                                        return;
                                    }
                                }
                            } else {
                                if (day > 31) {
                                    showErrDialog(this, "大月天数不超过31天！");
                                    return;
                                }
                            }
                        } else {
                            showErrDialog(this, "数字必须大于零");
                            return;
                        }
                        table.setValueAt(year + "." + month + "." + day, 0, 3);
                        setVisible(false);

                    } catch (NumberFormatException ex) {
                        showErrDialog(this, "年份不是有效整数！");
                    }
                });
                add(button_OK);
            }

            @NotNull
            private JComboBox<String> getComboBox_month() {
                JComboBox<String> comboBox_Month = new JComboBox<>();
                comboBox_Month.addItem("一月");
                comboBox_Month.addItem("二月");
                comboBox_Month.addItem("三月");
                comboBox_Month.addItem("四月");
                comboBox_Month.addItem("五月");
                comboBox_Month.addItem("六月");
                comboBox_Month.addItem("七月");
                comboBox_Month.addItem("八月");
                comboBox_Month.addItem("九月");
                comboBox_Month.addItem("十月");
                comboBox_Month.addItem("十一月");
                comboBox_Month.addItem("十二月");
                return comboBox_Month;
            }

            public void updateXY() {
                setLocation(Utils.centerXY(dialogDim.width, dialogDim.height, getReference()));
                validate();
            }

            private void resetInput() {
                Calendar c = Calendar.getInstance();
                textField_Year.setText(String.valueOf(c.get(Calendar.YEAR)));
                comboBox_Month.setSelectedIndex(c.get(Calendar.MONTH));
                comboBox_Day.setSelectedIndex(c.get(Calendar.DATE));
            }

            public void display() {
                resetInput();
                updateXY();
                setVisible(true);
            }
        }
    }
}
